/*
 * Controller.cpp
 *
 *  Created on: 30/10/2011
 *      Author: godinho
 */

#include "Controller.h"
#include "SimulationNode.h"
#include "Network.h"
#include "NodeInput.h"
#include "TLSLink.h"
#include "TLSPhase.h"
#include "SimulatorOutput.h"
#include "NodeSource.h"
#include "log.h"

#include <vector>
#include <utils.h>
#include <defaults.h>

Controller::Controller(Network* network) : mNetwork(network) {
	mSimulationEnded = false;
}

Controller::~Controller() {

}

void Controller::startSimulation() {
	mStep = 0;
	mainLoop();
}

void Controller::mainLoop() {

	mNetwork->normalizeNetwork();

	mSimulatorOutput->startSimulation(mNetwork);

	while(!mSimulationEnded) {
		mSimulatorOutput->startStep(mStep, mNetwork);
		simulateNodes();
		generateCars();
		mSimulatorOutput->endStep(mStep, mNetwork);
		mStep++;
	}

	mSimulatorOutput->endSimulation(mNetwork);
}

void Controller::simulateNodes() {
	std::vector<SimulationNode*>::iterator it;
	std::vector<SimulationNode*> nodes = mNetwork->getNodes();

	for(it = nodes.begin(); it != nodes.end(); it++) {
		updateTLS(*it);
		moveCarsNode(*it);
	}
}

void Controller::moveCarsNode(SimulationNode* node) {

	rsi::log("mesoscopic", __FILE__, __LINE__, "(step %d) Moving cars on queues of node %s", mStep, node->getId().c_str());

	std::map<SimulationNode*, NodeInput*> inputNodes = node->getNodeInputMap();
	std::map<SimulationNode*, NodeInput*>::iterator nodeInputIt;

	for(nodeInputIt = inputNodes.begin(); nodeInputIt != inputNodes.end(); nodeInputIt++){
		updateOnTripCars((*nodeInputIt).second, node);
	}

	if(!node->isBorder()) {
		//If at the end of this method execution, there are still cars on
		//toBeTransfered state, these cars are moved to node queue.
		moveCarsOnLinks(node);
	}

	//Change cars from toBeTransfered state to onQueueState
	NodeInput* currentNodeInput;
	for(nodeInputIt = inputNodes.begin(); nodeInputIt != inputNodes.end(); nodeInputIt++) {
		currentNodeInput = (*nodeInputIt).second;

		if(currentNodeInput == 0) {
			throw "Due to a supernatural reason, on nodeinput iteration we found an invalid reference to node input";
		}

		currentNodeInput->transferToQueue();
	}
}

void Controller::updateOnTripCars(NodeInput* ni, SimulationNode* node) {
	//Decrement time of cars that are on trip to a node to another, and change
	//to input queue those which arrived on its destiny.
	if(node->isBorder()) {
		//If node is on network border, there is no need to move cars to another
		//node, since there are no more cars.
		ni->popAll();
	}else {
		ni->decrementTimeStepToArrive();
	}
}

void Controller::moveCarsOnLinks(SimulationNode* node) {

	TLSPhase* currentPhase = node->getCurrentPhase();

	//Calculate number of cars that will use each link, this number is sorted
	//based on probability of each link to be selected.
	//Another assumption of this method is that all links has the same source
	std::vector<TLSLink*> links = currentPhase->getLinks();
	std::vector<TLSLink*>::iterator linksIt;

	int* sortedAmount = new int[links.size()];

	int sortedCarsAmount = 0;
	int totalAmount = 0;
	int i = 0;
	for(linksIt = links.begin(); linksIt != links.end(); linksIt++) {
		sortedCarsAmount = poison((*linksIt)->getFlush());
		sortedCarsAmount = sortedCarsAmount == 0 ? 1 : sortedCarsAmount;
		sortedAmount[i++] = sortedCarsAmount;
		totalAmount += sortedCarsAmount;
	}

	//Move cars from current node to its neighbors input queue
	int qntMoved = 0;
	i = 0;
	TLSLink* curLink;
	for(linksIt = links.begin(); linksIt != links.end(); linksIt++) {
		curLink = (*linksIt);

		qntMoved = curLink->move(((float)sortedAmount[i++])/((float)totalAmount));

		rsi::log("mesoscopic", __FILE__, __LINE__, "Moving cars on node %s using link (%s,%s), qnt: %d",
				node->getId().c_str(), curLink->getFrom()->getId().c_str(), curLink->getTo()->getId().c_str(),
				qntMoved);
	}

	delete sortedAmount;
}

void Controller::setOutput(SimulatorOutput* output) {
	mSimulatorOutput = output;
}

void Controller::updateTLS(SimulationNode* node) {
	rsi::log("mesoscopic", __FILE__, __LINE__, "Updating TLS for %s", node->getId().c_str());
	if(node->hasPhases() && node->decrementCurrentPhaseTime()) {
		int oldPhase = node->getCurrentPhase()->getId();
		node->nextPhase();

		rsi::log("mesoscopic", __FILE__, __LINE__, "Changing phase on node %s, from %d to %d",
				node->getId().c_str(), oldPhase, node->getCurrentPhase()->getId());
	}
}

void Controller::generateCars() {
	std::vector<NodeSource*>::iterator it;
	std::vector<NodeSource*> s = mNetwork->getSources();

	int n = 0;

	for(it = s.begin(); it != s.end(); it++) {
		n = (*it)->generate();
		rsi::log("mesoscopic", __FILE__, __LINE__, "(step %d) Generated %d cars on source %s,%s",
				mStep, n,
				(*it)->getNode()->getId().c_str(),
				(*it)->getTarget()->getId().c_str());
	}
}

void Controller::stopSimulation() {
	mSimulationEnded = true;
}
